package com.atom.plugin.cleaner

import com.android.build.gradle.AppExtension
import com.atom.plugin.core.AbstractPlugin
import com.atom.plugin.core.CorePlugin
import com.atom.plugin.core.Log
import com.atom.plugin.core.concurrent.Schedulers
import com.atom.plugin.core.ext.replaceAll
import org.gradle.api.Project
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.tree.*
import java.io.File
import java.io.InputStream
import java.util.concurrent.ExecutorService
import java.util.jar.JarEntry


class CodeClean2Plugin : CorePlugin<CodeCleanExtension>() {

    override fun getExtensionName(): String {
        return this.javaClass.simpleName
    }

    override fun getExtensionClass(): Class<CodeCleanExtension> {
        return CodeCleanExtension::class.java
    }

    override fun afterEvaluate(project: Project, app: AppExtension) {
        this.extension.enableUse =
            this.extension.enableUse && !this.extension.cleanPackets.isNullOrEmpty()
        if (this.extension.enableUse) {
            if (this.extension.cleanClasses.isNullOrEmpty()) {
                this.extension.cleanClasses = arrayOf("android/util/Log")
            } else {
                this.extension.cleanClasses = this.extension.cleanClasses?.map {
                    it.replaceAll("\\.", "/")
                }?.toTypedArray()
            }
            this.extension.cleanPackets = this.extension.cleanPackets?.map {
                it.replaceAll("\\.", "/")
            }?.toTypedArray()
        }
        super.afterEvaluate(project, app)
    }

    private fun removeLog(
        node: MethodInsnNode,
        resultDeleteList: MutableList<AbstractInsnNode>
    ) {
        var previous: AbstractInsnNode? = node
        while (previous != null) {
            if (previous is LabelNode) {
                break
            }
            previous = previous.previous
        }

        var next: AbstractInsnNode? = previous?.next
        while (next != null) {
            if (next is LabelNode) {
                break
            }
            next = next.next
            resultDeleteList.add(next.previous)
        }
        next?.also {
            resultDeleteList.add(next)
        }
    }

    override fun collectClassStream(inputStream: InputStream, isFromJar: Boolean) {

    }

    override fun transformClassStream(inputStream: InputStream): ByteArray {
        val reader = ClassReader(inputStream)
        val writer = ClassWriter(1)
        val node = ClassNode()
        reader.accept(node, ClassReader.EXPAND_FRAMES)
        Log.e("transformDir  #${reader.className}")
        node.methods.forEach { methodNode ->
            removeLogLabel(methodNode)
        }
        node.accept(writer)
        return writer.toByteArray()
    }

    fun removeLogLabel(methodNode: MethodNode) {
        Log.e("removeLogLabel  #${methodNode.name}")
        try {
            var t = methodNode.instructions.first
            val mustBeRemovedLabel: MutableList<AbstractInsnNode> = arrayListOf()
            while (t != null) {
                if (t is MethodInsnNode) {
                    if (t.owner == "android/util/Log") {
                        //从此往上找lable
                        var find = t
                        var hasLDC = false
                        var hasMethodInsnNode = false
                        var hasTypeInsnNode = false
                        while (find.previous != null) {
                            find = find.previous
                            if (find is LdcInsnNode) {
                                hasLDC = true
                            }
                            if (find is MethodInsnNode) {
                                hasMethodInsnNode = true
                            }
                            if (find is TypeInsnNode
                                && find.desc == "java/lang/StringBuilder"
                            ) {
                                hasTypeInsnNode = true
                            }
                            if (find is LabelNode) {
                                if (hasLDC && !hasMethodInsnNode && !hasTypeInsnNode) {
                                    mustBeRemovedLabel.add(find)
                                    break
                                }
                                if (hasMethodInsnNode && hasTypeInsnNode && hasLDC) {
                                    mustBeRemovedLabel.add(find)
                                    break
                                }
                            }
                        }
                    }
                }
                t = t.next
            }
            for (i in mustBeRemovedLabel.indices) {
                Log.e("\n")
                var item: AbstractInsnNode? = mustBeRemovedLabel[i]
                var findLog = false
                while (item != null) {
                    if (item is MethodInsnNode
                        && item.owner == "android/util/Log"
                    ) {
                        findLog = true
                    }
                    item = item.next
                    if (item.previous !is LabelNode
                        && item.previous !is FrameNode
                        && item.previous !is LineNumberNode
                    ) {
                        Log.e("remove:" + item.previous.javaClass.name + " findLog = " + findLog)
                        methodNode.instructions.remove(item.previous)
                    }
                    if (findLog && item is LabelNode) {
                        break
                    }
                }
            }
            mustBeRemovedLabel.clear()
        } catch (e: Exception) {
            Log.e(e.localizedMessage)
        }
    }
}